package com.dly.hope.servce.impl;

import com.dly.hope.constant.ExceptionConstant;
import com.dly.hope.constant.GlobalException;
import com.dly.hope.modle.FileInfo;
import com.dly.hope.repository.FileInfoRepository;
import com.dly.hope.ro.FileRo;
import com.dly.hope.servce.FileService;
import com.dly.hope.servce.JwtService;
import com.dly.hope.type.DataStatusEnum;
import com.dly.hope.utils.ExceptionPropertiesUtil;
import com.dly.hope.utils.FileUtil;
import com.dly.hope.vo.FileVo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.vavr.control.Try;
import jodd.util.StringPool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

/**
 * @author linyu.dai
 * @project myserve
 * @description 文件处理类
 * @creat 2022/8/30 10:06:52
 */
@Service
@Slf4j
public class FileServiceImpl implements FileService {

    @Value("${file.path}")
    private String filepath;

    @Value("${file.load.tokenPath}")
    private String loadTokenPath;

    /**
     * 全文件后缀名与Content-Type映射
     */
    private Map<String, String> EXTENSION_CONTYPE_MAP = Maps.newHashMap();

    @Resource
    private FileInfoRepository fileInfoRepository;

    @Resource
    private ExecutorService executorService;

    @Resource
    private JwtService jwtService;

    /**
     * 载入全文件后缀名与Content-Type映射
     */
    @PostConstruct //每次这个并注入时投入使用时 调用
    public void loadExtContMap() {
        Try.run(() -> {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream("filetype/extension-contype.mp")), StandardCharsets.UTF_8));
            String mp = null;
            while ((mp = bufferedReader.readLine()) != null) {
                String[] elements = StringUtils.splitByWholeSeparator(mp, "->");
                if (elements.length > 1) {
                    EXTENSION_CONTYPE_MAP.put(elements[0], elements[1]);
                }
            }
        }).onFailure(e -> log.error(this.getClass().getSimpleName(), e));
    }

    @Override
    public String getContentTypeByFileName(String fileName) {
        if(Objects.isNull(fileName)){
            return null;
        }
        //获取文件后缀
        String fileSuffix = FileUtil.getFileSuffix(fileName);
        return EXTENSION_CONTYPE_MAP.get(fileSuffix);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVo save(FileRo fileRo) {
        return Try.ofCallable(() -> {
            FileVo fileVo = new FileVo();
            ArrayList<String> ids = Lists.newArrayList();
            ArrayList<String> urls = Lists.newArrayList();
            for (MultipartFile file : fileRo.getMultipartFiles()) {
                FileInfo fileInfo = new FileInfo();
                fileInfo.setName(file.getOriginalFilename());
                fileInfo.setFileType(file.getContentType());
                fileInfo.setPath(getFullPath());
                fileInfo.setSize(String.valueOf(file.getSize()));
                //文件类型，就是文件用来干嘛的
                fileInfo.setFileClass(fileRo.getFileType());
                fileInfoRepository.save(fileInfo);
                //上传文件到本地
                upload(file.getInputStream(), fileInfo.getPath(), fileInfo.getId());
                ids.add(fileInfo.getId());
                urls.add(loadTokenPath + jwtService.signFile(fileInfo.getId()));
            }
            fileVo.setFileIds(ids);
            fileVo.setTokenUrls(urls);

            return fileVo;
        }).getOrElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.FILE_SAVE_ERROR));
    }

    @Override
    public String getLoad(String fileId) {
        return fileInfoRepository.findByIdAndDataStatus(fileId, DataStatusEnum.EFFICIENT.getCode())
                .map(fileInfo -> {
                    String fileToken = jwtService.signFile(fileInfo.getId());
                    return loadTokenPath + fileToken;
                }).orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.FILE_NULL));
    }

    /**
     * @Author linyu.dai
     * @Description 获取文件路径
     * @Date 2022/11/7 11:11
     * @Param []
     * @return java.lang.String
     */
    public String getFullPath() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = simpleDateFormat.format(new Date());
        return filepath + StringPool.SLASH + format + StringPool.SLASH;
    }

    /**
     * @Author linyu.dai
     * @Description 上传文件
     * @Date 2022/8/30 15:59
     * @Param [is, directory]
     * @return void
     */
    public void upload(InputStream is, String directory, String fileName){
        CompletableFuture.runAsync(() -> Try.of(() -> {

//            int length;
//            byte[] bytes = new byte[1024];
//            while ((length = is.read(bytes)) != -1){
//                os.write(bytes,0,length);
//            }
//            os.flush();
                    FileUtils.copyInputStreamToFile(is, new File(directory + StringPool.SLASH + fileName));

                    is.close();
                    return true;
                }).onFailure((e) -> log.error(e.getMessage()))
                .getOrElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.IO_ERROR)), executorService);
    }

    @Override
    public void load(HttpServletResponse response, String token) {
        String fileId = jwtService.unSignFile(token);
        fileIdLoad(response, fileId);
    }

    @Override
    public void fileIdLoad(HttpServletResponse response, String fileId) {
        fileInfoRepository.findByIdAndDataStatus(fileId, DataStatusEnum.EFFICIENT.getCode()).map(fileInfo -> Try.of(() -> {
            ServletOutputStream outputStream = response.getOutputStream();
            String filePath = fileInfo.getPath() + fileId;
            FileUtils.copyFile(new File(filePath), outputStream);
            outputStream.flush();
            outputStream.close();
            return true;
        }).onFailure((e) -> log.error(e.getMessage())).getOrElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.IO_ERROR))).orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.FILE_NULL));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String fileId) {
        fileInfoRepository.findByIdAndDataStatus(fileId,DataStatusEnum.EFFICIENT.getCode()).map(fileInfo -> {
            fileInfo.setDataStatus(DataStatusEnum.INVALID.getCode());
            return true;
        }).orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.FILE_NULL));
    }
}
